<?php
namespace Services;
/**
 * File holds class for account data manipulation
 *
 * PHP version 5.3.5
 *
 * @category   CM
 * @package    Services
 * @subpackage -
 * @author     markus karileet <markuskarileet@hotmail.com>
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL
 * @link       -
 */
class Account extends ORM {
	/**
	 * Method checks if account data in session id valid.
	 * @return \Entities\Account When check is successful, account info is 
	 * returned, false otherwise
	 * @throws \Exception
	 */
	public function checkToken() {
		$this->_logger->debug('Checking authentication token');
		$valid = false;
		if (isset($_SESSION['userid'])) {
		    //try to read user from DB using session parameters
		    $template = new \Entities\Account();
    		//get logged in user ID
    		$template->setId((int)$_SESSION['userid']);
    		//retrieve the user from database
    		$loggedUser = $this->select($template);
    		//if user was found, check if session data is tampered or not
    		if ($loggedUser) {
    			if (@$_SESSION['token'] == self::_generateToken(
                    $loggedUser->getId(), 
                    $loggedUser->getName(), 
                    $loggedUser->getLastlogin()
                )) {
    				//session data untampered, user really is user with 'userid'
    				$this->_logger->debug('Got valid token');
    				$valid = $loggedUser;
//removed this block since when userid is found, but tokens do not
//match, then its a hack!
    			/*} else if (!isset($_SESSION['token'])) {
    				$this->_logger->debug('Token not found, regular user!');*/
    			} else {
    			    //token is found but does not match with the one generated
    			    //from user data, so it is safe to say that
    				//session data is corrupt, unset session and return false
    				$this->_logger->info('Tokens do not match, clearing user session');
    				$this->clearUserData();
    			}
    		}
		}
		return $valid;
	}
	
	/**
	 * Method authentices user from username and password. When match from
	 * DB is found, user info session is set and account last login time is 
	 * updated
	 * @param array $post HTML POST array 
	 * @return \Entities\Account Account info on success, false otherwise
	 * @throws \Exception
	 */
	public function authenticate($post) {
		//Read user from DB using username and pass
		$this->_logger->debug('Authenticating user: ' . $post['name']);
		$template = new \Entities\Account();
		$template->setName(\Misc\Tools::cleanInput($post['name']));
		$template->setPassword(sha1($post['password']));
		$authUser = $this->select($template);
		//when user is found, set sessoin data
		if ($authUser) {
			$loginTime = time();
			$_SESSION['userid'] = $authUser->getId();
			$_SESSION['token'] = self::_generateToken(
                $authUser->getId(), 
                $authUser->getName(), 
                $loginTime
            );
			//update login time
			$authUser->setLastlogin($loginTime);
			$this->update($authUser);
			$this->_logger->debug('User is valid, logging in!');
		} else {
			$this->_logger->debug('Login unsuccessful!');
		}
		//return false or authenticated user
		return $authUser;
	}
	
	/**
	 * Method generates checksum token from user login info
	 * @param int $id User ID
	 * @param var $name User name
	 * @param int $time Last login time
	 * @return SHA1 hash from attributes
	 */
	private static function _generateToken($id, $name, $time) {
	    return sha1(sprintf('%d1%s2%s', $id, $name, $time));
	}
	
	/**
	 * Method unsets user session
	 */
	public function clearUserData() {
		unset($_SESSION['token']);
		unset($_SESSION['userid']);
	}
	
	/**
	 * Method updates user account. When newpassword element is set, system
	 * changes user password
	 * @param array $post HTTP POST
	 * @param \Entities\Account $user Account to update
	 * @return boolean true on success, false on error
	 * @throws \Exception
	 */
	public function prepareUpdate($post, $user) {
	    //user authentication failed at previous point (FIXME: maybe move this into view?)
	    if (!$user) {
	        throw new \Exception('User object not present!');
	    }
	    //since notify is checkbox, if set, then user wants to get notifications
		if (isset($post['notify'])) {
			$user->setNotification(\Enum\Notification::Email);
		} else {
			$user->setNotification(\Enum\Notification::No);
		}
		$user->setEmail(\Misc\Tools::cleanInput($post['email']));
		
		//when type is set, use it. Admins can set this, regular users do not
		//have this field, so this is unset
		if (isset($post['type'])) {
			$user->setType((int)$post['type']);
		}
		$user->setPhone(\Misc\Tools::cleanInput($post['phone']));
		//if new password is set, use it
		if (strlen(@$post['newpassword']) > 0) {
			$user->setPassword(sha1($post['newpassword']));
		}
		return $this->update($user);
	}
	
	/**
	 * Method inserts new account to database.
	 * @param var $post HTTP POST
	 * @return int Insert ID when successful
	 * @throws \Exception
	 */
	public function prepareInsert($post) {
	    $id = 0;
	    //only update when post is present and passwords match
		if(isset($post) && count($post) > 0 && $post['password'] == $post['r_password'] ) {
			$account = new \Entities\Account();
			$account->setContract("0"); //TODO: set contract in the future
			$account->setEmail(\Misc\Tools::cleanInput($post['email']));
			$account->setLastlogin("0");
			$account->setName(\Misc\Tools::cleanInput($post['name']));
			
			//since notify is checkbox, if set, then user wants to get notifications
			if (isset($post['notify'])) {
				$account->setNotification(\Enum\Notification::Email);
			} else {
				$account->setNotification(\Enum\Notification::No);
			}
			$account->setPassword(sha1($post['password']));
			$account->setGivenname(\Misc\Tools::cleanInput($post['givenname']));
			$account->setPhone(\Misc\Tools::cleanInput($post['phone']));
			$account->setType(\Enum\AccountType::Client);
			$id = $this->insert($account);
		} else {
		    $this->_logger->error('Insert failed since $_POST is empty or passwords did not match!');
		}
		return $id;
	}
	
	/**
	 * Method returns list of admins that want to get notifications
	 * @return array Array of accounts
	 * @throws \Exception
	 */
	public function getAdminsToNotify() {
		$tmpl = new \Entities\Account();
		$tmpl->setNotification(\Enum\Notification::Email);
		$tmpl->setType(\Enum\AccountType::Admin);
		return $this->selectMulti($tmpl);
	}
	
	/**
	 * Method returns all accounts
	 * @return arraay Array of accounts
	 * @throws \Exception 
	 */
	public function getAll() {
		$account = new \Entities\Account();
		$accounts = $this->selectMulti($account);
		return $accounts;
	}
}